home *** CD-ROM | disk | FTP | other *** search
- /*
- * Program: FREQ.C
- * Author: Philip VanBaren & Emil LAURENTIU
- * Date: 15 August 1993
- * Last modified: Tuesday, 05 August 1997
- *
- * Description: This program samples data from a sound card, performs an FFT,
- * and displays the result.
- * Can handle up to 2048 points (actually any size is possible
- * with a little fiddling of buffers to get around 64k limits).
- * (This restriction is given freq.h, and may be changed.)
- * On a 486/33 this code can perform and plot 1024-point and
- * below in nearly real-time at 44100kHz. (1024 FFT=31ms)
- *
- * The DOS portion of this code was written for Borland C, but should work
- * with other compilers if the graphics and console-io base functions are
- * changed appropriately.
- *
- * The source for specific graphics environments and sound cards may require
- * other packages. Refer to the specific files for more details.
- *
- * Most changes are required only in the sc_*.c and gr_*.c files.
- *
- * Copyright (C) 1995 Philip VanBaren (C) Emil LAURENTIU
- */
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <math.h>
- #include "freq.h"
- #include "fft.h"
- #include "extern.h"
- #include "display.h"
-
- /*
- * Table for approximating the logarithm.
- * These values are round(log2(index/16)*8192) for index=0:31
- */
-
- long ln[] = {
- -131072L, -32768L, -24576L, -19784L, -16384L, -13747L,
- -11592L, -9770L, -8192L, -6800L, -5555L, -4428L, -3400L,
- -2454L, -1578L, -763L, 0L, 716L, 1392L, 2031L, 2637L, 3214L,
- 3764L, 4289L, 4792L, 5274L, 5738L, 6184L, 6614L, 7029L, 7429L,
- 7817L };
- int f_dtmf[8] = {697, 770, 852, 941, 1209, 1336, 1477, 1633};
- int o_dtmf[8] = {18, 20, 22, 24, 31, 34, 37, 42};
- int sb_f_dtmf[8] = {457, 505, 558, 617, 396, 438, 484, 535};
- char matrix_dtmf[4][4] = {
- {'1', '2', '3', 'A'},
- {'4', '5', '6', 'B'},
- {'7', '8', '9', 'C'},
- {'*', '0', '#', 'D'} };
- int on_dtmf[8];
- char dtmf_nr[121];
- int p_dtmf;
- int last_i = -1;
- int active_freq, active_dtmf = 0;
- int active_ctcss = 0;
- int ctcss_nr;
- double f_ctcss[] = {
- 67.0, 69.4 , 71.9, 74.4, 77.0, 79.7, 82.5, 85.4, 88.5, 91.5,
- 94.8, 97.4, 100.0, 103.5, 107.2, 110.9, 114.8, 118.8, 123.0,
- 127.3, 131.8, 136.5, 141.3, 146.2, 151.4, 156.7, 159.8, 162.2,
- 165.5, 167.9, 171.3, 173.8, 177.3, 179.9, 183.5, 186.2, 189.9,
- 192.8, 196.6, 199.5, 203.5, 206.5, 210.7, 218.1, 225.7, 229.1,
- 233.6, 241.8, 250.3, 254.1 };
- unsigned long ctcss_act1 = 0xABFFFFFD;
- unsigned long ctcss_act2 = 0x0001DD2A;
- long m_ctcss;
- char sline[80];
- volatile int flag[BUFFERS]; /* Array of flags indicating fullness of
- * buffers */
- volatile int record_buffer; /* Pointer to next buffer to be filled */
- int queue_buffer; /* Pointer to next buffer to be queued */
- int process_buffer; /* Pointer to next buffer to be FFTed */
-
- short *fftdata; /* Array for FFT data */
- short *wind; /* Array storing windowing function */
-
- int x[WINDOW_RIGHT - WINDOW_LEFT + 1]; /* Array of bin #'s
- * displayed */
- int x2[WINDOW_RIGHT - WINDOW_LEFT + 1]; /* Array of terminal bin
- * #'s */
- int lasty[WINDOW_RIGHT - WINDOW_LEFT + 1]; /* Last y position for
- * FFT bins */
- unsigned int yscale[WINDOW_RIGHT - WINDOW_LEFT + 1]; /* Scaling factors */
- long *ybase; /* Scaling offset for log calculations */
-
- long *displayval;
- int shift = 0; /* Number of bits for gain shift */
- double shiftscale = 1; /* Multiplication factor that does this shift */
- float log_scalefactor; /* Scaling factor for log values */
- float disp_scalefactor; /* Display scalefactor for log values */
-
- void far *buffer[BUFFERS]; /* Buffers for gathering data */
-
- short *p1, *p2; /* Various indexing pointers */
- int *bri;
- long *pDisplayval;
- int *pLasty;
- long *pYbase;
- unsigned int *pYscale;
- int *pX, *pX2;
-
- unsigned char far *sample8;
- short far *sample16;
-
- long a2, root, mask; /* Variables for computing Sqrt/Log of
- * Amplitude^2 */
- long peak_amp; /* Peak amplitude found */
- int peak_index; /* Bin number of the peak amplitude */
- long back1, back2; /* Variables for differencing */
- char ini_file[100]; /* Filename for the ini file */
- int done = 0; /* Flag indicating program should exit */
-
- int
- main( int argc, char *argv[], char *environ[] )
- {
- int i, j, ind;
- int padX, padY;
- long y;
- int first;
- int key = 0;
-
- draw_init( );
-
- DOUT( "Getting the command line arguments" );
-
- /*
- * Check if the first parameter is an ini file name
- */
- if ( argc > 1 && argv[1][0] != '-' && argv[1][0] != '/' )
- {
- strncpy( ini_file, argv[1], sizeof( ini_file ) );
- first = 2;
- }
- else
- {
- strncpy( ini_file, "dtmf_fft.ini", sizeof( ini_file ) );
- first = 1;
- }
-
- /*
- * Parse the ini file and command line
- */
- DOUT( "Parsing the ini file" );
- parse_ini_file( );
-
- DOUT( "Parsing the command line" );
- parse_command( ( argc - first ), &argv[first], environ );
-
- /*
- * Initialize the buffer info for the maximum size we will encounter
- */
- DOUT( "Allocating the buffer space" );
- setup_buffers( MAX_LEN );
-
- DOUT( "Computing the window functions" );
- compute_window_function( );
-
- /*
- * Set up the required arrays in the FFT code.
- */
- DOUT( "Initializing the FFT code" );
- InitializeFFT( fftlen );
-
- /*
- * Initialize the graphics to 640x480 VGA mode
- */
- DOUT( "Setting the graphics mode" );
- setup_graphics( );
-
- setnormalpalette( );
- draw_fontcolor( TEXT_COLOR );
- draw_rectangle( WINDOW_LEFT - 2, WINDOW_TOP - 2,
- WINDOW_RIGHT + 2, WINDOW_BOTTOM + 2, BORDER_COLOR );
-
- DOUT( "Resetting the sound card" );
- reset_soundcard( );
-
- /*
- * Initalize the graph scales
- */
- setup_xscale( );
- amplitude_scale( );
-
- DOUT( "Drawing the header information" );
- update_header( );
-
- /*
- * Keep getting data and plotting it. A space will pause, a second space
- * will continue. Any other key will quit.
- */
- DOUT( "Entering data loop" );
- while ( !done )
- {
- /* Wait for current buffer to fill up, and check for an input */
- int input;
- key = draw_getkey( );
- #ifdef DEBUG_MODE
- i = 0;
- while ( i++ < 500 && !key )
- #else
- while ( ( !flag[process_buffer] || freeze ) && !key )
- #endif
- key = draw_getkey( );
- input = key;
- while ( input )
- {
- /* Grab and count repeated keystrokes */
- int repetitions = 1;
-
- key = input;
- input = draw_getkey( );
- while ( input == key )
- {
- repetitions++;
- input = draw_getkey( );
- }
- done |= process_input( key, repetitions );
- }
-
- if ( !key && !freeze )
- {
- int clip = 0;
- /*
- * Perform windowing on the data
- */
- p1 = fftdata;
- p2 = wind;
-
- if ( sample_size == 8 )
- {
- sample8 = ( unsigned char far * ) buffer[process_buffer];
- for ( i = 0; i < fftlen; i++ )
- {
- #ifdef DEBUG_MODE
- *sample8 = ( char ) rand( );
- #endif
- if ( ( *sample8 == 0 ) || ( *sample8 == 255 ) )
- clip = 1;
- *p1 = ( short ) ( ( ( ( long ) ( *sample8 ) - 128L ) * ( long ) ( *p2 ) ) >> 7 );
- sample8++;
- p1++;
- p2++;
- }
- }
- else
- {
- sample16 = ( short far * ) buffer[process_buffer];
-
- for ( i = 0; i < fftlen; i++ )
- {
- #ifdef DEBUG_MODE
- *sample16 = rand( );
- #endif
- if ( ( *sample16 == 32767 ) || ( *sample16 == -32768L ) )
- clip = 1;
- *p1 = ( short ) ( ( ( long ) ( *sample16 ) * ( long ) ( *p2 ) ) >> 15 );
- sample16++;
- p1++;
- p2++;
- }
- }
-
- if ( clip )
- draw_setpalette( 0, warn.red, warn.green, warn.blue );
- else
- draw_setpalette( 0, background.red, background.green, background.blue );
-
- /* Free up the buffer we just processed. */
- flag[process_buffer] = 0;
- if ( ++process_buffer >= BUFFERS )
- process_buffer = 0;
-
- /* Now that we have processed the buffer, queue it up again. */
- recordblock( buffer[queue_buffer] );
- if ( ++queue_buffer >= BUFFERS )
- queue_buffer = 0;
-
- /* The real meat of the code lies elsewhere! */
- RealFFT( fftdata );
-
- /* Use pointers for indexing to speed things up a bit. */
- bri = BitReversed;
- pDisplayval = displayval;
-
- for ( i = 0; i < fftlen / 2; i++ )
- {
- /* Compute the magnitude */
- register long re = fftdata[*bri];
- register long im = fftdata[( *bri ) + 1];
- register long root;
- if ( ( a2 = re * re + im * im ) < 0 )
- a2 = 0; /* Watch for possible overflow */
- /* Use higher resolution only for small values */
- if ( a2 > 4194304L )
- {
- root = 32;
- do
- {
- mask = a2 / root;
- root = ( root + mask ) >> 1;
- } while ( labs( root - mask ) > 1 );
- root *= 16;
- }
- else
- {
- root = 512;
- a2 *= 256;
- do
- {
- mask = a2 / root;
- root = ( root + mask ) >> 1;
- } while ( labs( root - mask ) > 1 );
- }
- *pDisplayval = root;
- bri++;
- pDisplayval++;
- }
- }
-
- if ( dtmf_mode )
- {
- active_freq = 0;
- for ( i = 0; i < 8; i++ )
- {
- /* ind = (int)( (double)(f_dtmf[i])*fftlen/SampleRate +.5 ); */
- if ( displayval[o_dtmf[i]] > 524288.0 * threshold_level )
- {
- on_dtmf[i] = 1;
- active_freq += ( i < 4 ) ? 1 : 10;
- }
- else
- on_dtmf[i] = 0;
- }
- if ( active_freq == 11 ) /* DTMF means 2 freq's active */
- {
- padX = 1 * on_dtmf[4] + 2 * on_dtmf[5] + 3 * on_dtmf[6] + 4 * on_dtmf[7] - 1;
- padY = 1 * on_dtmf[0] + 2 * on_dtmf[1] + 3 * on_dtmf[2] + 4 * on_dtmf[3] - 1;
- i = padX + 4 * padY;
- if( log_mode && !active_dtmf )
- fprintf( log_file, "%s - '", time_stamp() );
- if ( i != last_i || !active_dtmf )
- {
- dtmf_nr[p_dtmf] = matrix_dtmf[padY][padX];
- dtmf_nr[p_dtmf + 1] = 0;
- last_i = i;
- draw_fontcolor( LABEL_COLOR );
- draw_text_left( 156 + 8 * ( p_dtmf % 40 ), 60 + 10 * ( p_dtmf / 40 ),
- &dtmf_nr[p_dtmf] );
- if( log_mode )
- fprintf( log_file, "%c", dtmf_nr[p_dtmf] );
- p_dtmf++;
- if ( p_dtmf == 120 )
- {
- p_dtmf = 0;
- draw_bar( 156, 60, 476, 90, 0 );
- }
- }
- active_dtmf = 1;
- }
- else
- {
- if( log_mode && active_dtmf )
- fprintf( log_file, "'\n" );
- active_dtmf = 0;
- }
- }
-
- if ( ctcss_mode )
- {
- draw_bar( 200, 60, 240, 70, 0 );
- draw_fontcolor( LABEL_COLOR );
- draw_text_left( 200, 60, "off" );
- m_ctcss = 0L;
- for ( i = 0; i < CTCSS_MAX; i++ )
- {
- if( ( i < 32 ? ctcss_act1 >> i : ctcss_act2 >> (i-32) ) & 1 )
- {
- ind = ( int ) ( f_ctcss[i] * fftlen / SampleRate + .5 );
- if ( displayval[ind] > m_ctcss )
- {
- m_ctcss = displayval[ind];
- ctcss_nr = i;
- }
- }
- }
- if ( m_ctcss > 524288.0 * threshold_level )
- {
- draw_bar( 124, 60, 240, 70, 0 );
- draw_fontcolor( GRAPH_COLOR );
- sprintf( sline, "%5.1lf Hz", f_ctcss[ctcss_nr] );
- draw_text_left( 124, 60, sline );
- draw_fontcolor( LABEL_COLOR );
- draw_text_left( 200, 60, "on" );
- if( log_mode && (last_i != ctcss_nr || !active_ctcss ) )
- {
- fprintf( log_file, "%s - %5.1lf Hz\n", time_stamp(),
- f_ctcss[ctcss_nr] );
- last_i = ctcss_nr;
- }
- active_ctcss = 1;
- }
- else
- active_ctcss = 0;
- }
-
- {
- /*
- * Next, put this data up on the display
- */
- setup_vga( ); /* Prepare VGA for video update */
-
- pLasty = lasty;
- pX = x;
- pX2 = x2;
- pYscale = yscale;
- peak_amp = 0;
- peak_index = 0;
- y = WINDOW_BOTTOM;
- /* For linear amplitude mode */
- {
- int index, xval;
- for ( i = WINDOW_LEFT; i < WINDOW_RIGHT + 1; i++ )
- {
- /*
- * If this line is the same as the previous one, just use the
- * previous y value. Else go ahead and compute the value.
- */
- index = *pX;
- if ( index != -1 )
- {
- register long dv = displayval[index];
- if ( *pX2 ) /* Take the maximum of a set of bins */
- {
- for ( xval = index; xval < *pX2; xval++ )
- {
- if ( displayval[xval] > dv )
- {
- dv = displayval[xval];
- index = xval;
- }
- }
- }
- y = ( WINDOW_BOTTOM ) - ( ( dv * *pYscale ) >> shift );
- if ( y < WINDOW_TOP )
- y = WINDOW_TOP;
- if ( dv > peak_amp )
- {
- peak_amp = dv;
- peak_index = *pX;
- }
- }
- if ( y > *pLasty )
- {
- /* Draw a black line */
- unsigned char bit = ~( 0x80 >> ( i & 0x07 ) );
- unsigned int endbase = ( unsigned int ) ( y * 80 );
- unsigned int base = ( unsigned int ) ( *pLasty * 80 + ( i >> 3 ) );
- while ( base < endbase )
- {
- screen( base ) &= bit;
- base += 80;
- }
- }
- else
- {
- /* Draw a blue line. */
- unsigned char bit = 0x80 >> ( i & 0x07 );
- unsigned int endbase = ( unsigned int ) ( ( *pLasty + 1 ) * 80 );
- unsigned int base = ( unsigned int ) ( y * 80 + ( i >> 3 ) );
-
- while ( base < endbase )
- {
- screen( base ) |= bit;
- base += 80;
- }
- }
-
- *pLasty = ( unsigned int ) y;
- pDisplayval++;
- pX++;
- pX2++;
- pLasty++;
- pYscale++;
- }
- }
- cleanup_vga( ); /* Reset VGA for normal functions */
- }
-
- if ( display_peak )
- {
- char ach[20];
- sprintf( ach, "%7.1f", ( double ) SampleRate * peak_index / fftlen );
- draw_bar( PKX, PKY - 1, PKX + 63, PKY + _font_height, 0 );
- draw_text_left( PKX, PKY, ach );
- }
- }
- /*
- * Shut down the DMA system.
- */
- cleanup_soundcard( );
-
- cleanup_graphics( );
-
- if( log_mode )
- fclose( log_file );
-
- printf( "You have been using DTMF_FFT v 1.10 (dtmf & ctcss) " );
- #ifdef SC_SB8
- if ( Soundcard == SC_SB8 )
- printf( " in Soundblaster 8-bit mode." );
- #endif
- #ifdef SC_SB16
- if ( Soundcard == SC_SB16 )
- printf( " in Soundblaster 16-bit mode." );
- #endif
- printf( "\nCopyright (C) 1996 Philip VanBaren & "
- "(C) 1997 Emil Laurentiu (YO3GGH)" );
- return ( 0 );
- }
-